package com.cloudeasy.post.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cloudeasy.api.constant.PermissionConstant;
import com.cloudeasy.api.enums.ResultCodeEnum;
import com.cloudeasy.api.exception.CloudEasyException;
import com.cloudeasy.api.util.ResultUtil;
import com.cloudeasy.api.vo.PageResult;
import com.cloudeasy.api.vo.Result;
import com.cloudeasy.api.vo.post.*;
import com.cloudeasy.api.vo.user.UserInfoVO;
import com.cloudeasy.core.bean.JwtParser;
import com.cloudeasy.post.client.UserClient;
import com.cloudeasy.post.dto.ReviewDTO;
import com.cloudeasy.post.po.Post;
import com.cloudeasy.post.po.Reply;
import com.cloudeasy.post.po.Storey;
import com.cloudeasy.post.service.PostService;
import com.cloudeasy.post.service.ReplyService;
import com.cloudeasy.post.service.StoreyService;
import com.cloudeasy.post.service.expand.SimpleUserInfoService;
import com.cloudeasy.post.util.IPageUtil;
import com.cloudeasy.post.util.PostUtil;
import com.cloudeasy.post.util.ReplyUtil;
import com.cloudeasy.post.util.StoreyUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;


@RestController
@CrossOrigin
@RequestMapping("/reply")
public class ReplyController extends BaseController {
    private final PostService postService;

    private final ReplyService replyService;

    private final StoreyService storeyService;

    private final SimpleUserInfoService simpleUserInfoService;

    private final JwtParser jwtParser;

    private final UserClient userClient;

    @Autowired
    public ReplyController(PostService postService, ReplyService replyService, StoreyService storeyService, JwtParser jwtParser, UserClient userClient, SimpleUserInfoService simpleUserInfoService) {
        super(userClient);
        this.postService = postService;
        this.replyService = replyService;
        this.storeyService = storeyService;
        this.jwtParser = jwtParser;
        this.simpleUserInfoService = simpleUserInfoService;
        this.userClient = userClient;
    }

    /**
     * 评论
     *
     * @param authentication {@link Authentication}
     * @param postId         帖子Id
     * @param reviewDTO      {@link ReviewDTO}
     * @return {@link Result<Boolean>}
     */
    @PostMapping("/{postId}")
    public Result<Boolean> save(Authentication authentication,
                                @PathVariable("postId") String postId,
                                @RequestBody @Validated ReviewDTO reviewDTO) {
        String userId = (String) jwtParser.getOnBody(authentication, "userId");
        return ResultUtil.success(replyService.save(postId, userId, reviewDTO));
    }

    /**
     * 删除评论
     *
     * @param authentication {@link Authentication}
     * @param id             评论Id
     * @return {@link Result<Boolean>}
     */
    @DeleteMapping("/{id}")
    public Result<Boolean> remove(Authentication authentication,
                                  @PathVariable("id") String id) {
        String userId = (String) jwtParser.getOnBody(authentication, "userId");
        Reply reply = replyService.getById(id);
        if (reply == null) {
            throw new CloudEasyException(ResultCodeEnum.ID_NOT_EXISTS);
        }
        // 如果不是删除自己的评论
        if (!reply.getReviewerId().equals(userId)) {
            Post post = postService.getById(reply.getPostId());
            // 如果没有找到帖子，说明帖子已经被删除
            if (post == null) {
                return ResultUtil.success(replyService.removeByPostId(reply.getPostId()));
            }
            // 如果不是删除自己帖子的评论
            if (!post.getAuthorId().equals(userId)) {
                Result<Boolean> result = getBooleanResult(PermissionConstant.DELETE_REPLY, Collections.singletonList(post.getArea()), userId);
                if (!result.getCode().equals(ResultCodeEnum.SUCCESS.getCode())) {
                    return result;
                }
            }
        }
        return ResultUtil.success(replyService.removeById(reply));
    }

    /**
     * 获取自己的评论
     *
     * @param authentication {@link Authentication}
     * @return {@link List<OwnReplyVO>}的{@link Result}
     */
    @GetMapping
    public Result<List<OwnReplyVO>> ownReplyList(Authentication authentication) {
        SimpleUserInfoVO simpleUserInfoVO = simpleUserInfoService.obtainSimpleUserInfo(authentication);
        List<Reply> replies = replyService.listByReviewerId(simpleUserInfoVO.getId());
        return ResultUtil.success(obtain(replies, new OwnReplyProcesser()));
    }

    /**
     * 分页获取自己的评论
     *
     * @param authentication {@link Authentication}
     * @param page           页数
     * @param size           每页大小
     * @return {@link PageResult<OwnReplyVO>}的{@link Result}
     */
    @GetMapping("/{page}/{size}")
    public Result<PageResult<OwnReplyVO>> ownReplyPage(Authentication authentication,
                                                       @PathVariable("page") Integer page,
                                                       @PathVariable("size") Integer size) {
        SimpleUserInfoVO simpleUserInfoVO = simpleUserInfoService.obtainSimpleUserInfo(authentication);
        IPage<Reply> replyIPage = replyService.pageByReviewerId(simpleUserInfoVO.getId(), page, size);
        PageResult<OwnReplyVO> ownReplyVOPageResult = new PageResult<>();
        ownReplyVOPageResult.setTotal(replyIPage.getTotal())
                .setPages(replyIPage.getPages())
                .setRecords(obtain(replyIPage.getRecords(), new OwnReplyProcesser()));
        return ResultUtil.success(ownReplyVOPageResult);
    }

    /**
     * 获取指定Id的评论
     *
     * @param id 评论Id
     * @return {@link Result<PostVO>}
     */
    @GetMapping("/one/{id:\\d+}")
    public Result<ReplyVO> get(@PathVariable("id") String id) {
        Reply reply = replyService.getById(id);
        if (reply == null) {
            return ResultUtil.success();
        }
        List<SimpleUserInfoVO> simpleUserInfoVOS = simpleUserInfoService.obtainSimpleUserInfo(Collections.singletonList(reply.getReviewerId()));
        return ResultUtil.success(ReplyUtil.convert(Collections.singletonList(reply), simpleUserInfoVOS).get(0));
    }

    /**
     * 分页获取帖子的评论
     *
     * @param postId 帖子Id
     * @param type   评论类型
     * @param page   页数
     * @param size   每页大小
     * @return {@link PageResult<ReplyVO>}的{@link Result}
     */
    @GetMapping("/{postId}/{page}/{size}")
    public Result<PageResult<ReplyVO>> pageReply(@PathVariable("postId") String postId,
                                                 @RequestParam(value = "type", required = false) Integer type,
                                                 @RequestParam(value = "asc", required = false) String[] asc,
                                                 @RequestParam(value = "desc", required = false) String[] desc,
                                                 @PathVariable("page") Integer page,
                                                 @PathVariable("size") Integer size) {
        QueryWrapper<Reply> queryWrapper = ReplyUtil.compose(postId, type, asc, desc);
        IPage<Reply> replyTempIPage = IPageUtil.compose(page, size);
        IPage<Reply> replyIPage = replyService.page(replyTempIPage, queryWrapper);

        PageResult<ReplyVO> replyVOPageResult = new PageResult<>();
        replyVOPageResult.setTotal(replyIPage.getTotal())
                .setPages(replyIPage.getPages())
                .setRecords(obtain(replyIPage.getRecords(), new ReplyVOProcesser()));
        return ResultUtil.success(replyVOPageResult);
    }

    /**
     * 构建返回对象
     *
     * @param replies        {@link List<Reply>}
     * @param replyProcesser {@link ReplyProcesser}
     * @param <T>            {@link T}
     * @return {@link List<T>}
     */
    public <T> List<T> obtain(List<Reply> replies, ReplyProcesser<T> replyProcesser) {
        Map<String, List<Storey>> storeyMap = new HashMap<>();
        // 要查询的所有用户的Id
        Set<String> idSet = new HashSet<>();
        // 关联的帖子的Id
        Set<String> postIdSet = new HashSet<>();
        // 填充所有评论者Id
        idSet.addAll(replies.stream().map(reply -> {
            // 获取关联的帖子的Id
            postIdSet.add(reply.getPostId());
            if (reply.getStorey()) {
                // 获取具有回复的前两条回复
                List<Storey> storeys = storeyService.simpleListByReplyId(reply.getId()).getRecords();
                // 暂存到Map
                storeyMap.put(reply.getId(), storeys);
                // 填充所有回复的回复者Id
                idSet.addAll(storeys.stream().map(Storey::getAnswererId).collect(Collectors.toSet()));
            }
            return reply.getReviewerId();
        }).collect(Collectors.toSet()));
        // 获取所有关联的帖子
        if (postIdSet.size() == 0) {
            return Collections.emptyList();
        }
        List<Post> posts = new ArrayList<>(postService.listByIds(postIdSet));
        // 如果是组合帖子对象，需要将帖子用户的Id也填充
        idSet.addAll(posts.stream().map(Post::getAuthorId).collect(Collectors.toSet()));

        Result<List<UserInfoVO>> result = userClient.list(new ArrayList<>(idSet), null, null, null);
        if (!result.getCode().equals(ResultCodeEnum.SUCCESS.getCode())) {
            throw new CloudEasyException(Objects.requireNonNull(ResultCodeEnum.get(result.getCode())));
        }
        // 获取用户信息
        List<SimpleUserInfoVO> simpleUserInfoVOS = result.getData().stream().map(userInfoVO -> {
            SimpleUserInfoVO simpleUserInfoVO = new SimpleUserInfoVO();
            BeanUtils.copyProperties(userInfoVO, simpleUserInfoVO);
            return simpleUserInfoVO;
        }).collect(Collectors.toList());

        List<PostVO> postVOS = PostUtil.convert(posts, simpleUserInfoVOS);

        Map<String, PostVO> postVOMap = postVOS.stream().collect(Collectors.toMap(PostVO::getId, postVO -> postVO, (o, o2) -> o));

        // 组合
        return replies.stream().map(reply -> replyProcesser.apply(reply, storeyMap, simpleUserInfoVOS, postVOMap)).collect(Collectors.toList());
    }

    private interface ReplyProcesser<T> {
        /**
         * 应用操作
         *
         * @param reply             {@link Reply}
         * @param storeyMap         value为{@link List<Storey>}的{@link Map}
         * @param simpleUserInfoVOS {@link List<SimpleUserInfoVO>}
         * @param postVOMap         {@link Map<String, PostVO>}
         * @return {@link T}
         */
        T apply(Reply reply, Map<String, List<Storey>> storeyMap, List<SimpleUserInfoVO> simpleUserInfoVOS, Map<String, PostVO> postVOMap);
    }

    private class OwnReplyProcesser implements ReplyProcesser<OwnReplyVO> {
        @Override
        public OwnReplyVO apply(Reply reply, Map<String, List<Storey>> storeyMap, List<SimpleUserInfoVO> simpleUserInfoVOS, Map<String, PostVO> postVOMap) {
            OwnReplyVO ownReplyVO = new OwnReplyVO();
            BeanUtils.copyProperties(reply, ownReplyVO);
            List<Storey> storeys = storeyMap.get(reply.getId());
            if (storeys != null && storeys.size() > 0) {
                ownReplyVO.setStoreys(StoreyUtil.convert(storeys, simpleUserInfoVOS));
            }
            PostVO postVO = postVOMap.get(reply.getPostId());
            ownReplyVO.setPostId(postVO.getId())
                    .setPostTitle(postVO.getTitle())
                    .setPostArea(postVO.getArea());
            simpleUserInfoVOS.forEach(simpleUserInfoVO -> {
                if (simpleUserInfoVO.getId().equals(reply.getReviewerId())) {
                    ownReplyVO.setReviewer(simpleUserInfoVO);
                }
            });
            return ownReplyVO;
        }
    }

    private class ReplyVOProcesser implements ReplyProcesser<ReplyVO> {
        @Override
        public ReplyVO apply(Reply reply, Map<String, List<Storey>> storeyMap, List<SimpleUserInfoVO> simpleUserInfoVOS, Map<String, PostVO> postVOMap) {
            ReplyVO replyVO = ReplyUtil.convert(Collections.singletonList(reply), simpleUserInfoVOS).get(0);
            List<Storey> storeys = storeyMap.get(reply.getId());
            replyVO.setStoreys(StoreyUtil.convert(storeys, simpleUserInfoVOS));
            return replyVO;
        }
    }
}
